package com.lu.integrate.message.center.config;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.lu.stack.common.exception.InvalidTokenException;
import com.lu.stack.common.exception.LuAssertException;
import com.lu.stack.common.model.response.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.nio.file.AccessDeniedException;
import java.util.Iterator;
import java.util.Set;

/**
 * @program lu-boot-saas
 * @description: 全局异常
 * @author: zhanglu
 * @create: 2022-09-26 14:25:00
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

  /*************************参数校验异常*************************/

  @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
  @ExceptionHandler(value = BindException.class)
  public R<String> violationException(BindException exception) {
    // 不带任何参数访问接口,会抛出 BindException
    // 因此，我们只需捕获这个异常，并返回我们设置的 message 即可
    String message = exception.getAllErrors().get(0).getDefaultMessage();
    return R.failed(HttpStatus.METHOD_NOT_ALLOWED.value(), message);
  }

  /**
   * Get请求的参数校验，抛出的是ConstraintViolationException
   * @param ex
   * @return
   */
  @ExceptionHandler({ConstraintViolationException.class})
  public R<String> constraintViolationException(ConstraintViolationException ex) {
    Set<ConstraintViolation<?>> eSet = ex.getConstraintViolations();
    StringBuffer sb = new StringBuffer();
    if(!CollectionUtils.isEmpty(eSet)) {
      Iterator<ConstraintViolation<?>> iterator = eSet.iterator();
      while (iterator.hasNext()) {
        sb.append(iterator.next().getMessage()).append("::");
      }
    }
    return R.failed(HttpStatus.METHOD_NOT_ALLOWED.value(), "server failed");
  }

  /**
   * Post请求Bean中的校验抛出:MethodArgumentNotValidException
   * @param ex
   * @return
   */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public R handleBindException(MethodArgumentNotValidException ex) {
    FieldError fieldError = ex.getBindingResult().getFieldError();
    return R.failed(fieldError.getDefaultMessage());
  }

  /**
   * 方法签名参数错误
   * @param ex
   * @return
   */
  @ExceptionHandler(MissingServletRequestParameterException.class)
  public R handleGetBindException(MissingServletRequestParameterException ex) {
    return R.failed(ex.getMessage());
  }

  /*************************其他异常*************************/

  @ExceptionHandler(value = IllegalArgumentException.class)
  public R<String> illegalParameterException(IllegalArgumentException e) {
    String message = e.getMessage();
    log.info("IllegalArgumentException ==> {}", message);
    return R.failed(message);
  }

  @ResponseStatus(HttpStatus.UNAUTHORIZED)
  @ExceptionHandler(value = InvalidTokenException.class)
  public R<String> invalidTokenException(InvalidTokenException e) {
    String message = e.getMessage();
    log.info("lu-boot InvalidTokenException ==> {}", message);
    return R.failed(message);
  }

  @ResponseStatus(HttpStatus.FORBIDDEN)
  @ExceptionHandler(value = AccessDeniedException.class)
  public R<String> accessDeniedException(AccessDeniedException e) {
    log.error("lu-boot AccessDeniedException：" + e);
    return R.failed("当前权限不足!");
  }


  /**
   * 运行时异常
   *
   * @param e 异常
   * @return 响应结果
   */
  @ExceptionHandler(value = LuAssertException.class)
  public R<String> luAssertException(LuAssertException e) {
    log.error("lu-boot HxflAssertException：" + e);
    return R.failed(e.getMessage());
  }

  /**
   * 运行时异常
   *
   * @param e 异常
   * @return 响应结果
   */
  @ExceptionHandler(value = RuntimeException.class)
  public R<String> runtimeException(RuntimeException e) {
    log.error("lu-boot RuntimeException：" + e);
    return R.failed("服务繁忙");
  }

  /**
   * 全局异常兜底处理
   *
   * @param e 异常
   * @return 响应结果
   */
  @ExceptionHandler(value = Exception.class)
  public R<String> exception(Exception e) {
    log.error("lu-boot Exception：" + e);
    return R.failed("服务繁忙");
  }

}
